import React, { useState, useReducer } from 'react';

import styles from './try_immer.less';
import produce, { Draft, createDraft, isDraft, current } from 'immer';
import { tuple } from 'antd/lib/_util/type';

interface ICount {
  count: number;
  is: {
    active: boolean;
  };
}

// 原始的 reducer
function counterReducer(state: ICount, action: any) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

interface ICounterReducer {
  (state: ICount, action: any): ICount;
}

// immer 版本的 reducer
const counterReducerImmer: ICounterReducer = produce(
  (state: Draft<ICount>, action: any) => {
    console.log(action);
    switch (action.type) {
      case 'increment':
        let s = state;
        s.count++;
        let is = s.is;
        is.active = !is.active; // 使用赋值出去的引用，依然 immer 有效
        break;
      case 'decrement':
        state.count--;
        break;
      default:
        throw new Error();
    }
  },
);

function asyncFn(seconds: number = 1000) {
  return new Promise(resolve => setTimeout(() => resolve(undefined), seconds));
}

/**
 * yield undefined 代表使用 current(draft) setState, 返回 isDraft(v)=true 直接使用 current(draft)
 * @param draft
 * @param action
 */
function* gReducer(draft: ICount, action: any) {
  switch (action.type) {
    case 'increment':
      draft.count++;
      yield;
      draft.count++;
      yield asyncFn().then(() => (draft.count *= 2));
      yield asyncFn().then(() => (draft.count /= 2));
      yield asyncFn(2000);
      draft.count += 10;
      yield draft;
      break;
    case 'decrement':
      draft.count--;
      break;
    default:
      throw new Error();
  }
}

function isPromise(v: any) {
  if (v && v.then) return true;
  return false;
}

/**
 * 执行给定的 generator reducer 函数，初始状态，每步 next 后对结果执行 callback
 * @param gReducer generator 方式的 reducer
 * @param initState 开始状态
 * @param action 变更指令
 * @param callback 每步执行完的回调
 */
async function executeWithCallback(
  gReducer: any,
  initState: any,
  action: any,
  callback: any,
) {
  const ge = gReducer(initState, action);
  while (true) {
    const result = ge.next();
    const nextState = result.value;
    if (isPromise(nextState)) {
      await nextState;
      callback();
    } else {
      callback(nextState);
    }
    if (result.done) return;
  }
}

// executeWithCallback(gReducer, { count: 0 }, { type: 'increment' }, console.log);

/**
 * 使用 generator yield {nextState: current(draft)}
 * @param init
 * @param g
 */
function useGeneratorReducer(init: any, g: Function) {
  const [state, setState] = useState(init);
  // 把上面的 setState 改成 dispatch({type:"setState"}) 即可
  function dispatch(action: any) {
    const draft = createDraft(state);
    let seq = 0;
    console.log('seq', seq);
    executeWithCallback(g, draft, action, (v: any) => {
      console.log('seq', ++seq, v, current(draft));
      if (v === undefined) {
        // when yield nothing
        setState(current(draft));
      } else if (isDraft(v)) {
        // when yield draft
        setState(current(v));
      }
    });
  }
  return [state, dispatch];
}

export default function() {
  const [counter, counterDispatch] = useReducer(counterReducerImmer, {
    count: 0,
    is: { active: true },
  });
  const [state, dispatch] = useGeneratorReducer({ count: 10 }, gReducer);
  return (
    <div className={styles.normal}>
      <h1 onClick={() => counterDispatch({ type: 'increment' })}>
        Page try_immer {counter.count} is {counter.is.active || 'not'} active
      </h1>
      <h1 onClick={() => dispatch({ type: 'increment' })}>
        Page try_immer {state && state.count}
      </h1>
    </div>
  );
}
